﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/15 18:25:51
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImageK.Gui;
using ImageK.Measure;
using ImageK.Process;
using ImageK.Util;

namespace ImageK.Plugin.Frame
{
    /// <summary>
    /// This plugin implements the Brightness/Contrast, Window/level and
    /// Color Balance commands, all in the Image/Adjust sub-menu. It
    /// allows the user to interactively adjust the brightness  and
    /// contrast of the active image. It is multi-threaded to
    /// provide a more  responsive user interface.
    /// </summary>
    public class ContrastAdjuster:PlugInDialog
    {
        public const string LOC_KEY = "b&c.loc";
        public string[] sixteenBitRanges = new string[] {"Automatic", "8-bit (0-255)", "10-bit (0-1023)",
            "12-bit (0-4095)", "14-bit (0-16383)", "15-bit (0-32767)", "16-bit (0-65535)"};
        const int AUTO_THRESHOLD = 5000;
        
        Thread thread;
        private static ContrastAdjuster instance;
        int minSliderValue = -1, maxSliderValue = -1, brightnessValue = -1, contrastValue = -1;
        int sliderRange = 256;
        bool doAutoAdjust, doReset, doSet, doApplyLut;

        int previousImageID;
        int previousType;
        int previousSlice = 1;
        ImageK ij;
        double min, max;
        double previousMin, previousMax;
        double defaultMin, defaultMax;
        int contrast, brightness;
        bool RGBImage;

        bool done;
        int autoThreshold;

        bool windowLevel, balance;

        int channels = 7; // RGB

        private string blankLabel8 = "--------";
        private string blankLabel12 = "------------";
        private double scale = Prefs.getGuiScale();
        private int digits;


        private ContrastPlot _plot = new ContrastPlot();
        private Panel PlotPanel;
        private TrackBar MinSlider;
        private Label label1;
        private Label label2;
        private TrackBar MaxSlider;
        private Label label3;
        private TrackBar BrightnessSlider;
        private Label label4;
        private TrackBar ContrastSlider;
        private Button AutoB;
        private Button ResetB;
        private Button SetB;
        private Button ApplyB;
        private Label MinLabel;
        private Label MaxLabel;
        static readonly object _locker = new object();

        public ContrastAdjuster()
        {
            InitializeComponent();
            Text = "B&C";
        }


        public override void run(string arg)
        {
            windowLevel = arg.Equals("wl");
            balance = arg.Equals("balance");
            if (windowLevel)
                Text = "W&L";
            else if (balance)
            {
                Text = "Color";
                channels = 4;
            }

            if (instance != null)
            {
                if (!instance.Text.Equals(Text))
                {
                    ContrastAdjuster ca = instance;
                    // Prefs.saveLocation(LOC_KEY, ca.getLocation());
                    ca.Close();
                }
                else
                {
                    instance.BringToFront();
                    return;
                }
            }
            instance = this;
            // IJ.register(ContrastAdjuster.class);
            WindowManager.addWindow(this);

            ij = IJ.GetInstance();
            
            PlotPanel.Controls.Add(_plot);

            Show();

            thread = new Thread(Run)
            {
                Name = "ContrastAdjuster"
            };
            thread.Start();
            setup();
        }

        private void InitializeComponent()
        {
            this.PlotPanel = new System.Windows.Forms.Panel();
            this.MinSlider = new System.Windows.Forms.TrackBar();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.MaxSlider = new System.Windows.Forms.TrackBar();
            this.label3 = new System.Windows.Forms.Label();
            this.BrightnessSlider = new System.Windows.Forms.TrackBar();
            this.label4 = new System.Windows.Forms.Label();
            this.ContrastSlider = new System.Windows.Forms.TrackBar();
            this.AutoB = new System.Windows.Forms.Button();
            this.ResetB = new System.Windows.Forms.Button();
            this.SetB = new System.Windows.Forms.Button();
            this.ApplyB = new System.Windows.Forms.Button();
            this.MinLabel = new System.Windows.Forms.Label();
            this.MaxLabel = new System.Windows.Forms.Label();
            ((System.ComponentModel.ISupportInitialize)(this.MinSlider)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.MaxSlider)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.BrightnessSlider)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.ContrastSlider)).BeginInit();
            this.SuspendLayout();
            // 
            // PlotPanel
            // 
            this.PlotPanel.Location = new System.Drawing.Point(12, 12);
            this.PlotPanel.Name = "PlotPanel";
            this.PlotPanel.Size = new System.Drawing.Size(131, 72);
            this.PlotPanel.TabIndex = 0;
            // 
            // MinSlider
            // 
            this.MinSlider.Location = new System.Drawing.Point(11, 104);
            this.MinSlider.Name = "MinSlider";
            this.MinSlider.Size = new System.Drawing.Size(131, 45);
            this.MinSlider.TabIndex = 1;
            this.MinSlider.TickStyle = System.Windows.Forms.TickStyle.None;
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(61, 123);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(62, 17);
            this.label1.TabIndex = 2;
            this.label1.Text = "Minimum";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(61, 162);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(65, 17);
            this.label2.TabIndex = 4;
            this.label2.Text = "Maximum";
            // 
            // MaxSlider
            // 
            this.MaxSlider.Location = new System.Drawing.Point(11, 143);
            this.MaxSlider.Name = "MaxSlider";
            this.MaxSlider.Size = new System.Drawing.Size(131, 45);
            this.MaxSlider.TabIndex = 3;
            this.MaxSlider.TickStyle = System.Windows.Forms.TickStyle.None;
            // 
            // label3
            // 
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(61, 201);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(69, 17);
            this.label3.TabIndex = 6;
            this.label3.Text = "Brightness";
            // 
            // BrightnessSlider
            // 
            this.BrightnessSlider.Location = new System.Drawing.Point(11, 182);
            this.BrightnessSlider.Name = "BrightnessSlider";
            this.BrightnessSlider.Size = new System.Drawing.Size(131, 45);
            this.BrightnessSlider.TabIndex = 5;
            this.BrightnessSlider.TickStyle = System.Windows.Forms.TickStyle.None;
            // 
            // label4
            // 
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(61, 240);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(57, 17);
            this.label4.TabIndex = 8;
            this.label4.Text = "Contrast";
            // 
            // ContrastSlider
            // 
            this.ContrastSlider.Location = new System.Drawing.Point(11, 221);
            this.ContrastSlider.Name = "ContrastSlider";
            this.ContrastSlider.Size = new System.Drawing.Size(131, 45);
            this.ContrastSlider.TabIndex = 7;
            this.ContrastSlider.TickStyle = System.Windows.Forms.TickStyle.None;
            // 
            // AutoB
            // 
            this.AutoB.Location = new System.Drawing.Point(3, 269);
            this.AutoB.Name = "AutoB";
            this.AutoB.Size = new System.Drawing.Size(75, 23);
            this.AutoB.TabIndex = 9;
            this.AutoB.Text = "Auto";
            this.AutoB.UseVisualStyleBackColor = true;
            this.AutoB.Click += new System.EventHandler(this.ActionPerformed);
            // 
            // ResetB
            // 
            this.ResetB.Location = new System.Drawing.Point(79, 269);
            this.ResetB.Name = "ResetB";
            this.ResetB.Size = new System.Drawing.Size(75, 23);
            this.ResetB.TabIndex = 10;
            this.ResetB.Text = "Reset";
            this.ResetB.UseVisualStyleBackColor = true;
            this.ResetB.Click += new System.EventHandler(this.ActionPerformed);
            // 
            // SetB
            // 
            this.SetB.Location = new System.Drawing.Point(3, 295);
            this.SetB.Name = "SetB";
            this.SetB.Size = new System.Drawing.Size(75, 23);
            this.SetB.TabIndex = 11;
            this.SetB.Text = "Set";
            this.SetB.UseVisualStyleBackColor = true;
            this.SetB.Click += new System.EventHandler(this.ActionPerformed);
            // 
            // ApplyB
            // 
            this.ApplyB.Location = new System.Drawing.Point(79, 295);
            this.ApplyB.Name = "ApplyB";
            this.ApplyB.Size = new System.Drawing.Size(75, 23);
            this.ApplyB.TabIndex = 12;
            this.ApplyB.Text = "Apply";
            this.ApplyB.UseVisualStyleBackColor = true;
            this.ApplyB.Click += new System.EventHandler(this.ActionPerformed);
            // 
            // MinLabel
            // 
            this.MinLabel.AutoSize = true;
            this.MinLabel.Location = new System.Drawing.Point(12, 87);
            this.MinLabel.Name = "MinLabel";
            this.MinLabel.Size = new System.Drawing.Size(18, 17);
            this.MinLabel.TabIndex = 13;
            this.MinLabel.Text = "--";
            // 
            // MaxLabel
            // 
            this.MaxLabel.AutoSize = true;
            this.MaxLabel.Location = new System.Drawing.Point(94, 87);
            this.MaxLabel.Name = "MaxLabel";
            this.MaxLabel.Size = new System.Drawing.Size(18, 17);
            this.MaxLabel.TabIndex = 13;
            this.MaxLabel.Text = "--";
            // 
            // ContrastAdjuster
            // 
            this.ClientSize = new System.Drawing.Size(156, 321);
            this.Controls.Add(this.MaxLabel);
            this.Controls.Add(this.ApplyB);
            this.Controls.Add(this.MinLabel);
            this.Controls.Add(this.SetB);
            this.Controls.Add(this.ResetB);
            this.Controls.Add(this.AutoB);
            this.Controls.Add(this.label4);
            this.Controls.Add(this.ContrastSlider);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.BrightnessSlider);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.MaxSlider);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.MinSlider);
            this.Controls.Add(this.PlotPanel);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "ContrastAdjuster";
            ((System.ComponentModel.ISupportInitialize)(this.MinSlider)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.MaxSlider)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.BrightnessSlider)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.ContrastSlider)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }


        void setup()
        {
            ImagePlus imp = WindowManager.getCurrentImage();
            if (imp != null)
            {
                if (imp.getType() == ImagePlus.COLOR_RGB && imp.isLocked())
                    return;
                setup(imp);
                updatePlot();
                updateLabels(imp);
                imp.updateAndDraw();
            }
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            instance = null;
            done = true;
            // Prefs.saveLocation(LOC_KEY, getLocation());
            lock(_locker) {
                Monitor.Pulse(_locker);
            }
        }

        private void ActionPerformed(object sender, EventArgs e)
        {
            Button b = (Button)sender;
            if (b == ResetB)
            {
                doReset = true;
            }
            else if (b == AutoB)
                doAutoAdjust = true;
            else if (b == SetB)
                doSet = true;
            else if (b == ApplyB)
                doApplyLut = true;

            lock (_locker)
            {
                Monitor.Pulse(_locker);
            }
            
        }

        ImageProcessor setup(ImagePlus imp)
        {
            Roi roi = imp.getRoi();
            if (roi != null) roi.endPaste();
            ImageProcessor ip = imp.getProcessor();
            int type = imp.getType();
            int slice = imp.getCurrentSlice();
            RGBImage = type == ImagePlus.COLOR_RGB;
            if (imp.getID() != previousImageID || type != previousType || slice != previousSlice)
                setupNewImage(imp, ip);
            previousImageID = imp.getID();
            previousType = type;
            previousSlice = slice;
            return ip;
        }


        void setupNewImage(ImagePlus imp, ImageProcessor ip)
        {
            Undo.reset();
            previousMin = min;
            previousMax = max;
            bool newRGBImage = RGBImage && !((ColorProcessor)ip).caSnapshot();
            if (newRGBImage)
            {
                //todo:
                ip.snapshot();
                // ((ColorProcessor)ip).caSnapshot(true);
            }
            double min2 = imp.getDisplayRangeMin();
            double max2 = imp.getDisplayRangeMax();
            if (newRGBImage)
            {
                min2 = 0.0;
                max2 = 255.0;
            }
            int bitDepth = imp.getBitDepth();
            if (bitDepth == 16 || bitDepth == 32)
            {
                Roi roi = imp.getRoi();
                imp.deleteRoi();
                ImageStatistics stats = imp.getRawStatistics();
                defaultMin = stats.min;
                defaultMax = stats.max;
                imp.setRoi(roi);
            }
            else
            {
                defaultMin = 0;
                defaultMax = 255;
            }
            setMinAndMax(imp, min2, max2);
            min = imp.getDisplayRangeMin();
            max = imp.getDisplayRangeMax();
            _plot.defaultMin = defaultMin;
            _plot.defaultMax = defaultMax;
            int valueRange = (int)(defaultMax - defaultMin);
            int newSliderRange = valueRange;
            if (newSliderRange > 640 && newSliderRange < 1280)
                newSliderRange /= 2;
            else if (newSliderRange >= 1280)
                newSliderRange /= 5;
            if (newSliderRange < 256) newSliderRange = 256;
            if (newSliderRange > 1024) newSliderRange = 1024;
            double displayRange = max - min;
            if (valueRange >= 1280 && valueRange != 0 && displayRange / valueRange < 0.25)
                newSliderRange = (int)(newSliderRange * 1.6666);
            if (newSliderRange != sliderRange)
            {
                sliderRange = newSliderRange;
                this.SafeInvoke(()=>
                {
                    updateScrollBars(null, true);
                });
                
            }
            else
            {
                this.SafeInvoke(() =>
                {
                    updateScrollBars(null, false);
                });
            }

            if (balance)
            {
                //todo:
                // if (imp.isComposite())
                // {
                //     int channel = imp.getChannel();
                //     if (channel <= 4)
                //     {
                //         choice.select(channel - 1);
                //         channels = channelConstants[channel - 1];
                //     }
                //     if (choice.getItem(0).equals("Red"))
                //     {
                //         choice.removeAll();
                //         addBalanceChoices();
                //     }
                // }
                // else
                // { // not composite
                //     if (choice.getItem(0).equals("Channel 1"))
                //     {
                //         choice.removeAll();
                //         addBalanceChoices();
                //     }
                // }
            }
            if (!doReset)
                plotHistogram(imp);
            autoThreshold = 0;
            // if (imp.isComposite())
            //     IJ.setKeyUp(KeyEvent.VK_SHIFT);
        }

        const int RESET = 0, AUTO = 1, SET = 2, APPLY = 3, THRESHOLD = 4, MIN = 5, MAX = 6,
            BRIGHTNESS = 7, CONTRAST = 8, UPDATE = 9;

        private void Run()
        {
            while (!done)
            {
                lock(_locker) {
                    try
                    {
                        Monitor.Wait(_locker);
                    }
                    catch (ThreadInterruptedException e)
                    {
                    }
                }
                doUpdate();
            }
        }

        void doUpdate()
        {
            ImagePlus imp;
            ImageProcessor ip;
            int action;
            int minvalue = minSliderValue;
            int maxvalue = maxSliderValue;
            int bvalue = brightnessValue;
            int cvalue = contrastValue;
            if (doReset) action = RESET;
            else if (doAutoAdjust) action = AUTO;
            else if (doSet) action = SET;
            else if (doApplyLut) action = APPLY;
            else if (minSliderValue >= 0) action = MIN;
            else if (maxSliderValue >= 0) action = MAX;
            else if (brightnessValue >= 0) action = BRIGHTNESS;
            else if (contrastValue >= 0) action = CONTRAST;
            else return;
            minSliderValue = maxSliderValue = brightnessValue = contrastValue = -1;
            doReset = doAutoAdjust = doSet = doApplyLut = false;
            imp = WindowManager.getCurrentImage();
            if (imp == null)
            {
                IJ.beep();
                IJ.ShowStatus("No image");
                return;
            }
            //todo:
            // else if (imp.getOverlay() != null && imp.getOverlay().isCalibrationBar())
            // {
            //     IJ.beep();
            //     IJ.ShowStatus("Has calibration bar");
            //     return;
            // }
            ip = imp.getProcessor();
            if (RGBImage && !imp.Lock()) {
                imp = null;
                return;
            }
            switch (action)
            {
                case RESET:
                    reset(imp, ip);
                    // if (Recorder.record)
                    // {
                    //     if (Recorder.scriptMode())
                    //         Recorder.recordCall("IJ.resetMinAndMax(imp);");
                    //     else
                    //         Recorder.record("resetMinAndMax");
                    // }
                    break;
                case AUTO:
                    autoAdjust(imp, ip);
                    break;
                // case SET:
                //     if (windowLevel) setWindowLevel(imp, ip);
                //     else setMinAndMax(imp, ip);
                //     break;
                // case APPLY:
                //     apply(imp, ip);
                //     break;
                // case MIN:
                //     adjustMin(imp, ip, minvalue);
                //     break;
                // case MAX:
                //     adjustMax(imp, ip, maxvalue);
                //     break;
                // case BRIGHTNESS:
                //     adjustBrightness(imp, ip, bvalue);
                //     break;
                // case CONTRAST:
                //     adjustContrast(imp, ip, cvalue);
                //     break;
            }

            this.SafeInvoke(() =>
            {
                updatePlot();
                updateLabels(imp);

                if ((IJ.shiftKeyDown() || (balance && channels == 7)) && imp.isComposite())
                {
                    //todo:
                    // ((CompositeImage)imp).updateAllChannelsAndDraw();
                }
                else
                {
                    imp.updateChannelAndDraw();
                }
            });

                
            if (RGBImage)
                imp.Unlock();
        }

        void plotHistogram(ImagePlus imp)
        {
            ImageStatistics stats = null;
            if (balance && (channels == 4 || channels == 2 || channels == 1) && imp.getType() == ImagePlus.COLOR_RGB)
            {
                //todo:
                // int w = imp.getWidth();
                // int h = imp.getHeight();
                // byte[] r = new byte[w * h];
                // byte[] g = new byte[w * h];
                // byte[] b = new byte[w * h];
                // ((ColorProcessor)imp.getProcessor()).getRGB(r, g, b);
                // byte[] pixels = null;
                // if (channels == 4)
                //     pixels = r;
                // else if (channels == 2)
                //     pixels = g;
                // else if (channels == 1)
                //     pixels = b;
                // ImageProcessor ip = new ByteProcessor(w, h, pixels, null);
                // stats = ImageStatistics.getStatistics(ip, 0, imp.getCalibration());
            }
            else
            {
                int range = imp.getType() == ImagePlus.GRAY16 ? ImagePlus.getDefault16bitRange() : 0;
                if (range != 0 && imp.getProcessor().getMax() == Math.Pow(2, range) - 1 && !(imp.getCalibration().isSigned16Bit()))
                {
                    ImagePlus imp2 = new ImagePlus("Temp", imp.getProcessor());
                    stats = new StackStatistics(imp2, 256, 0, Math.Pow(2, range));
                }
                else
                {
                    // stats = imp.getStatistics();
                }

            }
            Color color = Color.Gray;
            if (imp.isComposite() && !(balance && channels == 7))
            {
                //todo:
                // color = ((CompositeImage)imp).getChannelColor();
            }
                
            _plot.setHistogram(stats, color);
        }


        void setMinAndMax(ImagePlus imp, double min, double max)
        {
            bool rgb = imp.getType() == ImagePlus.COLOR_RGB;
            if (channels != 7 && rgb)
                imp.setDisplayRange(min, max, channels);
            else
                imp.setDisplayRange(min, max);
            if (rgb)
                plotHistogram(imp);
        }

        void updatePlot()
        {
            _plot.min = min;
            _plot.max = max;
            _plot.Refresh();
        }

        void updateLabels(ImagePlus imp)
        {
            double min = imp.getDisplayRangeMin();
            double max = imp.getDisplayRangeMax();
            ;
            int type = imp.getType();
            Calibration cal = imp.getCalibration();
            bool realValue = type == ImagePlus.GRAY32;
            if (cal.calibrated())
            {
                min = cal.getCValue((int)min);
                max = cal.getCValue((int)max);
                realValue = true;
            }
            if (windowLevel)
            {
                digits = realValue ? 3 : 0;
                double window = max - min;
                double level = min + (window) / 2.0;
                // windowLabel.setText(ResultsTable.d2s(window, digits));
                // levelLabel.setText(ResultsTable.d2s(level, digits));
            }
            else
            {
                digits = realValue ? 4 : 0;
                if (realValue)
                {
                    double s = min < 0 || max < 0 ? 0.1 : 1.0;
                    double amin = Math.Abs(min);
                    double amax = Math.Abs(max);
                    if (amin > 99.0 * s || amax > 99.0 * s) digits = 3;
                    if (amin > 999.0 * s || amax > 999.0 * s) digits = 2;
                    if (amin > 9999.0 * s || amax > 9999.0 * s) digits = 1;
                    if (amin > 99999.0 * s || amax > 99999.0 * s) digits = 0;
                    if (amin > 9999999.0 * s || amax > 9999999.0 * s) digits = -2;
                    if ((amin > 0 && amin < 0.001) || (amax > 0 && amax < 0.001)) digits = -2;
                }
                string minString = IJ.d2s(min, min == 0.0 ? 0 : digits) + blankLabel8;
                MinLabel.Text = minString.Substring(0, blankLabel8.Length);
                string maxString = blankLabel8 + IJ.d2s(max, digits);
                maxString = maxString.Substring(maxString.Length - blankLabel8.Length);
                MaxLabel.Text = maxString;
            }
        }

        void updateScrollBars(TrackBar sb, bool newRange)
        {
            if (sb == null || sb != ContrastSlider)
            {
                double mid = sliderRange / 2;
                double c = ((defaultMax - defaultMin) / (max - min)) * mid;
                if (c > mid)
                    c = sliderRange - ((max - min) / (defaultMax - defaultMin)) * mid;
                contrast = (int)c;
                if (ContrastSlider != null)
                {
                    if (newRange)
                    {
                        ContrastSlider.Minimum = 0;
                        ContrastSlider.Maximum = sliderRange;
                        ContrastSlider.Value = contrast;
                        // ContrastSlider.visibleAmount = 1

                    }
                    else
                        ContrastSlider.Value =contrast;
                }
            }
            if (sb == null || sb != BrightnessSlider)
            {
                double level = min + (max - min) / 2.0;
                double normalizedLevel = 1.0 - (level - defaultMin) / (defaultMax - defaultMin);
                brightness = (int)(normalizedLevel * sliderRange);
                brightness = Math.Max(0, brightness);
                if (newRange)
                {
                    BrightnessSlider.Minimum = 0;
                    BrightnessSlider.Maximum = sliderRange;
                    BrightnessSlider.Value = brightness;
                    // BrightnessSlider.visibleAmount = 1

                }
                else
                    BrightnessSlider.Value = brightness;
            }
            if (MinSlider != null && (sb == null || sb != MinSlider))
            {
                if (newRange)
                {
                    MinSlider.Minimum = 0;
                    MinSlider.Maximum = sliderRange;
                    MinSlider.Value = scaleDown(min);
                    // MinSlider.visibleAmount = 1

                }
                else
                    MinSlider.Value = scaleDown(min);
            }
            if (MaxSlider != null && (sb == null || sb != MaxSlider))
            {
                if (newRange)
                {
                    MaxSlider.Minimum = 0;
                    MaxSlider.Maximum = sliderRange;
                    MaxSlider.Value = scaleDown(max);
                    // MaxSlider.visibleAmount = 1

                }
                else
                    MaxSlider.Value = scaleDown(max);
            }
        }

        int scaleDown(double v)
        {
            if (v < defaultMin) v = defaultMin;
            if (v > defaultMax) v = defaultMax;
            return (int)((v - defaultMin) * (sliderRange - 1.0) / (defaultMax - defaultMin));
        }

        /**
         * auto histogram and contrast
         *
         * @param imp
         * @param ip
         */
        void autoAdjust(ImagePlus imp, ImageProcessor ip)
        {
            if (RGBImage)
                ip.reset();
            //获取图像数据
            ImageStatistics stats = imp.getRawStatistics();
            int limit = stats.pixelCount / 10;
            int[] histogram = stats.Histogram;
            if (autoThreshold < 10)
                autoThreshold = AUTO_THRESHOLD;
            else
                autoThreshold /= 2;
            int threshold = stats.pixelCount / autoThreshold;
            int i = -1;
            bool found = false;
            int count;
            do
            {
                i++;
                count = histogram[i];
                if (count > limit) count = 0;
                found = count > threshold;
            } while (!found && i < 255);
            int hmin = i;
            i = 256;
            do
            {
                i--;
                count = histogram[i];
                if (count > limit) count = 0;
                found = count > threshold;
            } while (!found && i > 0);
            int hmax = i;
            Roi roi = imp.getRoi();
            if (hmax >= hmin)
            {
                //todo:
                // if (RGBImage) imp.deleteRoi();
                min = stats.histMin + hmin * stats.binSize;
                max = stats.histMin + hmax * stats.binSize;
                if (min == max)
                {
                    min = stats.min;
                    max = stats.max;
                }
                setMinAndMax(imp, min, max);

                //todo:
                // if (RGBImage && roi != null) imp.setRoi(roi);
            }
            else
            {
                reset(imp, ip);
                return;
            }
            this.SafeInvoke(() =>
            {
                updateScrollBars(null, false);
            });
            if (Recorder.OnRecord)
            {
                //todo:
                // if (Recorder.scriptMode())
                //     Recorder.recordCall("IJ.run(imp, \"Enhance Contrast\", \"saturated=0.35\");");
                // else
                //     Recorder.record("run", "Enhance Contrast", "saturated=0.35");
            }
        }

        void reset(ImagePlus imp, ImageProcessor ip)
        {
            if (RGBImage)
                ip.reset();
            int bitDepth = imp.getBitDepth();
            if (bitDepth == 16 || bitDepth == 32)
            {
                imp.resetDisplayRange();
                defaultMin = imp.getDisplayRangeMin();
                defaultMax = imp.getDisplayRangeMax();
                _plot.defaultMin = defaultMin;
                _plot.defaultMax = defaultMax;
            }
            min = defaultMin;
            max = defaultMax;
            setMinAndMax(imp, min, max);
            this.SafeInvoke(() =>
            {
                updateScrollBars(null, false);
            });
            
            plotHistogram(imp);
            autoThreshold = 0;
        }
    }
}
